// clear;g++ -o helloCurl helloworld.cc -lcurl -L ../build/lib -I ../include/ -Wl,-rpath=../build/lib;./helloCurl

#include <iostream>
#include <ctime>
#include <ratio>
#include <chrono>
#include <thread>
#include <vector>

#include <string.h>
#include <assert.h>

#include "curl/curl.h"
// #include "urldata.h"

struct HeaderData {
  size_t size = 0;
  std::vector<std::string> headerVec;
};
struct memory {
  char *response = nullptr;
  size_t size = 0;
  ~memory() {
    if (response) {
      free(response);
      response = nullptr;
    }
  }
};

static size_t HeaderCallback(char *buffer, size_t size,
                              size_t nitems, void *userdata)
{
  /* received header is nitems * size long in 'buffer' NOT ZERO TERMINATED */
  /* 'userdata' is set with CURLOPT_HEADERDATA */
  // std::cout << __LINE__ << userdata << std::endl;
  HeaderData *headerPtr = static_cast<HeaderData *>(userdata);
  headerPtr->size += size;
  headerPtr->headerVec.emplace_back(std::string(buffer, nitems));
  return nitems * size;
}
static size_t WriteDataCallback(void *data, size_t size, size_t nmemb, void *userp)
{
  std::cout << __LINE__ << userp << std::endl;
  size_t realsize = size * nmemb;
  struct memory *mem = (struct memory *)userp;

  char *ptr = static_cast<char *>(realloc(static_cast<void *>(mem->response), mem->size + realsize + 1));
  if(ptr == NULL)
    return 0;  /* out of memory! */

  mem->response = ptr;
  memcpy(&(mem->response[mem->size]), data, realsize);
  mem->size += realsize;
  mem->response[mem->size] = 0;

  return realsize;
}
static size_t WriteStringCallback(void *data, size_t size, size_t nmemb, void *userp)
{
  std::cout << __LINE__ << userp << std::endl;
  std::string *str = static_cast<std::string *>(userp);

  str->append(static_cast<char *>(data), nmemb);

  return size * nmemb;
}

struct progress {
  char *ptr;
  size_t size;
};
 
static size_t ProgressCallback(void *clientp,
                                curl_off_t dltotal,
                                curl_off_t dlnow,
                                curl_off_t ultotal,
                                curl_off_t ulnow)
{
  struct progress *memory = (struct progress *)clientp;
  std::cout << "dltotal: " << dltotal << std::endl;
  std::cout << "dlnow: " << dlnow << std::endl;
  std::cout << "ultotal: " << ultotal << std::endl;
  std::cout << "ulnow: " << ulnow << std::endl;
  /* use the values */

  return 0; /* all is good */
}
int HelloCURL() {
  CURL *curl = curl_easy_init();
  struct curl_slist *list = NULL;
  if(curl) {
    std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
    assert(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1) == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "http://122.9.208.127:9066/knowledge/learningPractices/pageList?id=44&pageSize=9&pageNum=1") == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "122.9.208.127:9066/knowledge/learningPractices/pageList?id=44&pageSize=9&pageNum=1") == CURLE_OK);
    assert(curl_easy_setopt(curl, CURLOPT_URL, "https://example.com") == CURLE_OK);
    
    struct curl_slist *host = NULL;
    // host = curl_slist_append(host, "test.com:443:93.184.216.34");
    // curl_easy_setopt(curl, CURLOPT_RESOLVE, host);

    // assert(curl_easy_setopt(curl, CURLOPT_URL, "https://93.184.216.34:443") == CURLE_OK);
    curl_easy_setopt(curl, CURLOPT_LOCALPORT, 49152L);
    curl_easy_setopt(curl, CURLOPT_LOCALPORTRANGE, 20L);
    assert(curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L) == CURLE_OK);
    assert(curl_easy_setopt(curl, CURLOPT_PORT, 9066) == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L) == CURLE_OK); // connections marked connect only will not be allowed to get reused.
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "https://blog.csdn.net/alwaysrun/article/details/108905119") == CURLE_OK);
    // list = curl_slist_append(list, "Cache-Control: no-cache");
    // list = curl_slist_append(list, "Expires: 0");
    // list = curl_slist_append(list, "Connection: close");

    curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); // http1.0 默认短链接 1.1默认长链接
    list = curl_slist_append(list, "Connection: keep-alive");
    // curl_easy_setopt(curl, CURLOPT_TRANSFER_ENCODING, 1L);

    // assert(curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1) == CURLE_OK);    // 没有效果？？？
    // assert(curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 10L) == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 5L) == CURLE_OK);

    list = curl_slist_append(list, "Accept:");
    list = curl_slist_append(list, "XXXX;");
    // list = curl_slist_append(list, "Host: example.com");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
    curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
    
    /* get the first 200 bytes */
    assert(curl_easy_setopt(curl, CURLOPT_RANGE, "0-199") == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_HEADER, 1) == CURLE_OK); // header data get passed to the write callback
    assert(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0) == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_DEBUGDATA, 1) == CURLE_OK);
    assert(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback) == CURLE_OK);
    HeaderData *headerPtr = new HeaderData();
    if (!headerPtr) {
        return -1;
    }
    // std::cout << __LINE__ << headerPtr << std::endl;
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerPtr);
    struct memory chunk = {nullptr, 0};
    // std::cout << __LINE__ << &chunk << std::endl;
    // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteDataCallback);
    // curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);

    std::string respond;
    std::cout << __LINE__ << &respond << std::endl;
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteStringCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&respond);

      
    struct progress data;

    /* pass struct to callback  */
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    curl_easy_setopt(curl, CURLOPT_XFERINFODATA , &data);
    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback);

    /* Perform the request */
    assert(curl_easy_perform(curl) == CURLE_OK);
    CURL *nother = curl_easy_duphandle(curl);
    // std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
    // assert(curl_easy_perform(curl) == CURLE_OK);
    std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
    // std::this_thread::sleep_for (std::chrono::seconds(10));
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "https://blog.csdn.net/alwaysrun/") == CURLE_OK);
    std::chrono::high_resolution_clock::time_point t3 = std::chrono::high_resolution_clock::now();
    // std::cout << "================================================================" << std::endl;
    // curl_easy_perform(curl);
    // std::chrono::high_resolution_clock::time_point t4 = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> time_span0 = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1);
    // std::chrono::duration<double> time_span1 = std::chrono::duration_cast<std::chrono::duration<double>>(t4 - t3);
    std::cout << std::endl;
    std::cout << "time_span0: " << time_span0.count() << std::endl;
    // std::cout << "time_span1: " << time_span1.count() << std::endl;
    
    std::cout << headerPtr->size << std::endl;
    for (auto & header : headerPtr->headerVec) {
      std::cout << __LINE__ << " " << header << std::endl;
    }
    std::cout << respond.size() << std::endl;
    // std::cout << chunk.response << std::endl;
    std::cout << respond << std::endl;
    // for (auto &ch : headerPtr->headerVec[headerPtr->headerVec.size() - 1]) {
    //   std::cout << static_cast<int>(ch) << " " << ch << std::endl;
    // }
    std::cout << __LINE__ << std::endl;
    
    curl_easy_cleanup(curl);
    curl_slist_free_all(list);
    curl_slist_free_all(host);

    delete headerPtr;
  }
  return 0;
}

void MimeType() {
  CURL *easy = curl_easy_init();
  curl_mime *mime;
  curl_mimepart *part;
  assert(curl_easy_setopt(easy, CURLOPT_VERBOSE, 1) == CURLE_OK);
  
  /* Build an HTTP form with a single field named "data", */
  mime = curl_mime_init(easy);
  part = curl_mime_addpart(mime);
  curl_mime_data(part, "This is the field data", CURL_ZERO_TERMINATED);
  curl_mime_name(part, "XXXXXXXXXXX");
  
  /* Post and send it. */
  curl_easy_setopt(easy, CURLOPT_MIMEPOST, mime);
  curl_easy_setopt(easy, CURLOPT_URL, "https://example.com");
  curl_easy_perform(easy);
  
  /* Clean-up. */
  curl_easy_cleanup(easy);
  curl_mime_free(mime);
}
static int prereq_callback(void *clientp,
                           char *conn_primary_ip,
                           char *conn_local_ip,
                           int conn_primary_port,
                           int conn_local_port)
{
  // printf("%d Connection made to %s:%s\n", *(static_cast<int *>(clientp)), conn_primary_ip, conn_primary_port);
  if (conn_primary_ip) {
    std::cout << "@@@@@@@@@@ " << conn_primary_ip << std::endl;
    std::cout << "@@@@@@@@@@ " << conn_local_ip << std::endl;
    std::cout << "@@@@@@@@@@ " << conn_primary_port << std::endl;
  }
  std::cout << *(static_cast<int *>(clientp)) << " ****************************** " << conn_local_port << std::endl;
  return CURL_PREREQFUNC_OK;
}
static int resolver_start_cb(void *resolver_state, void *reserved,
                             void *userdata)
{
  (void)reserved;
  printf("@@@@@@@@@@ Received resolver_state=%p userdata=%p\n",
         resolver_state, userdata);
  return 0;
}
// curlsocktype ;
// curl_socket_t ;
static int closesocket(void *clientp, curl_socket_t item)
{
  printf("libcurl wants to close %d now\n", (int)item);
  return 0;
}
/* call this function to close sockets */
int PostCURL() {
  const char *data = "data to send";
  CURL *curl = curl_easy_init();
  struct curl_slist *list = NULL;
  if(curl) {
    std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
    assert(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1) == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "http://122.9.208.127:9066/") == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "122.9.208.127:9066/knowledge/learningPractices/pageList?id=44&pageSize=9&pageNum=1") == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "https://www.baidu.com") == CURLE_OK);
    assert(curl_easy_setopt(curl, CURLOPT_URL, "http://121.5.63.248:8000") == CURLE_OK);

    int prereq_data = 57;
    curl_easy_setopt(curl, CURLOPT_PREREQFUNCTION, prereq_callback);
    curl_easy_setopt(curl, CURLOPT_PREREQDATA, (void *)&prereq_data);

    // assert(curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L) == CURLE_OK);

    // curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "CONNECT");
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");

    struct curl_slist *host = NULL;
    // host = curl_slist_append(host, "test.com:443:93.184.216.34");
    // curl_easy_setopt(curl, CURLOPT_RESOLVE, host);

    // assert(curl_easy_setopt(curl, CURLOPT_URL, "https://93.184.216.34:443") == CURLE_OK);
    curl_easy_setopt(curl, CURLOPT_LOCALPORT, 49152L);
    curl_easy_setopt(curl, CURLOPT_LOCALPORTRANGE, 20L);
    // assert(curl_easy_setopt(curl, CURLOPT_PORT, 80) == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L) == CURLE_OK); // connections marked connect only will not be allowed to get reused.
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "https://blog.csdn.net/alwaysrun/article/details/108905119") == CURLE_OK);
    // list = curl_slist_append(list, "Cache-Control: no-cache");
    // list = curl_slist_append(list, "Expires: 0");
    // list = curl_slist_append(list, "Connection: close");

    curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); // http1.0 默认短链接 1.1默认长链接
    list = curl_slist_append(list, "Connection: keep-alive");
    // curl_easy_setopt(curl, CURLOPT_TRANSFER_ENCODING, 1L);

    // assert(curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L) == CURLE_OK);    // 没有效果？？？
    // assert(curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L) == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 30L) == CURLE_OK);
    curl_easy_setopt(curl, CURLOPT_MAXAGE_CONN, 240);

    list = curl_slist_append(list, "Accept:");
    list = curl_slist_append(list, "XXXX;");
    // list = curl_slist_append(list, "Host: example.com");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
    curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");

    /* get the first 200 bytes */
    // assert(curl_easy_setopt(curl, CURLOPT_RANGE, "0-199") == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_HEADER, 1) == CURLE_OK); // header data get passed to the write callback
    assert(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0) == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_DEBUGDATA, 1) == CURLE_OK);
    assert(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback) == CURLE_OK);
    HeaderData *headerPtr = new HeaderData();
    if (!headerPtr) {
        return -1;
    }
    // std::cout << __LINE__ << headerPtr << std::endl;
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerPtr);
    struct memory chunk = {nullptr, 0};
    // std::cout << __LINE__ << &chunk << std::endl;
    // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteDataCallback);
    // curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);

    std::string respond;
    std::cout << __LINE__ << &respond << std::endl;
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteStringCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&respond);

    struct progress data1;

    /* pass struct to callback  */
    // curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    // curl_easy_setopt(curl, CURLOPT_XFERINFODATA , &data1);
    // curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback);

    curl_easy_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_start_cb);
    curl_easy_setopt(curl, CURLOPT_RESOLVER_START_DATA, curl);
    
    int sockfd;
    curl_easy_setopt(curl, CURLOPT_CLOSESOCKETFUNCTION, closesocket);
    curl_easy_setopt(curl, CURLOPT_CLOSESOCKETDATA, &sockfd);

    /* Perform the request */
    CURLcode ret = curl_easy_perform(curl);
    if (ret != CURLE_OK) {
      std::cout << "ret CURLcode:" << ret << std::endl;
    }

    // CURL *nother = curl_easy_duphandle(curl);
    // std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
    // assert(curl_easy_perform(curl) == CURLE_OK);
    std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
    std::this_thread::sleep_for (std::chrono::seconds(130));
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "https://blog.csdn.net/alwaysrun/") == CURLE_OK);
    std::chrono::high_resolution_clock::time_point t3 = std::chrono::high_resolution_clock::now();
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "122.9.208.127:9066/knowledge/learningPractices/pageList?id=44&pageSize=9&pageNum=1") == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "https://example.com") == CURLE_OK);
    // curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
    // curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 12L);
    /* pass in a pointer to the data - libcurl will not copy */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
    std::cout << "================================================================" << std::endl;
    curl_easy_perform(curl);
    // std::chrono::high_resolution_clock::time_point t4 = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> time_span0 = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1);
    // std::chrono::duration<double> time_span1 = std::chrono::duration_cast<std::chrono::duration<double>>(t4 - t3);
    std::cout << std::endl;
    std::cout << "time_span0: " << time_span0.count() << std::endl;
    // std::cout << "time_span1: " << time_span1.count() << std::endl;
    
    std::cout << headerPtr->size << std::endl;
    for (auto & header : headerPtr->headerVec) {
      std::cout << __LINE__ << " " << header << std::endl;
    }
    // std::cout << respond.size() << std::endl;
    // std::cout << chunk.response << std::endl;
    std::cout << respond << std::endl;
    // for (auto &ch : headerPtr->headerVec[headerPtr->headerVec.size() - 1]) {
    //   std::cout << static_cast<int>(ch) << " " << ch << std::endl;
    // }
    long response_code;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
    std::cout << "CURLINFO_RESPONSE_CODE:" << response_code << std::endl;
    long code;
    curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE, &code);
    std::cout << "CURLINFO_HTTP_CONNECTCODE:" << code << std::endl;
    long sockfd1;
    curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockfd1);
    std::cout << "CURLINFO_LASTSOCKET:" << sockfd1 << std::endl;
    std::cout << __LINE__ << std::endl;
    curl_easy_cleanup(curl);
    curl_slist_free_all(list);
    curl_slist_free_all(host);

    delete headerPtr;
  }
  return 0;
}

/* call this function to close sockets */
int ReUseCURL() {
  const char *data = "data to send";
  CURL *curl = curl_easy_init();
  struct curl_slist *list = NULL;
  if(curl) {
    std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
    assert(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1) == CURLE_OK);
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "http://121.5.63.248:8000") == CURLE_OK);
    assert(curl_easy_setopt(curl, CURLOPT_URL, "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E9%9F%A9%E9%9B%AA%E6%B4%81&fenlei=256&rsv_pq=b2faecea000da088&rsv_t=f3b8Mm6XCkBwZvwDKHAry9w1LMjt6rw0QP8lnK62focC9OsBQShLyFjUXwY3&rqlang=en&rsv_enter=1&rsv_dl=ib&rsv_sug3=2&rsv_sug1=1&rsv_sug7=001&rsv_n=2") == CURLE_OK);

    int prereq_data = 57;
    curl_easy_setopt(curl, CURLOPT_PREREQFUNCTION, prereq_callback);
    curl_easy_setopt(curl, CURLOPT_PREREQDATA, (void *)&prereq_data);

    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");

    struct curl_slist *host = NULL;

    curl_easy_setopt(curl, CURLOPT_LOCALPORT, 49152L);
    curl_easy_setopt(curl, CURLOPT_LOCALPORTRANGE, 20L);

    curl_easy_setopt(curl, CURLOPT_MAXAGE_CONN, 240); // keep http client alive 240s.

    curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");

    assert(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0) == CURLE_OK);
    assert(curl_easy_setopt(curl, CURLOPT_DEBUGDATA, 1) == CURLE_OK);
    assert(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback) == CURLE_OK);
    HeaderData *headerPtr = new HeaderData();
    if (!headerPtr) {
        return -1;
    }
    // std::cout << __LINE__ << headerPtr << std::endl;
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerPtr);

    std::string respond;
    std::cout << __LINE__ << &respond << std::endl;
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteStringCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&respond);

    curl_easy_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_start_cb);
    curl_easy_setopt(curl, CURLOPT_RESOLVER_START_DATA, curl);
    
    int sockfd;
    curl_easy_setopt(curl, CURLOPT_CLOSESOCKETFUNCTION, closesocket);
    curl_easy_setopt(curl, CURLOPT_CLOSESOCKETDATA, &sockfd);
    CURLSH *share;
    share = curl_share_init();
    curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
    curl_easy_setopt(curl, CURLOPT_SHARE, share);

    /* Perform the request */
    CURLcode ret = curl_easy_perform(curl);
    if (ret != CURLE_OK) {
      std::cout << "ret CURLcode:" << ret << std::endl;
    }

    std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
    std::this_thread::sleep_for (std::chrono::seconds(13));
    // assert(curl_easy_setopt(curl, CURLOPT_URL, "https://blog.csdn.net/alwaysrun/") == CURLE_OK);
    std::chrono::high_resolution_clock::time_point t3 = std::chrono::high_resolution_clock::now();
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");

    std::cout << "================================================================" << std::endl;
    // curl_easy_perform(curl);
    CURL *nother = curl_easy_duphandle(curl);
    // CURL *nother = curl_easy_init();
    // CURLSH *share;
    // share = curl_share_init();
    // curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
    // curl_easy_setopt(curl, CURLOPT_SHARE, share);
    curl_easy_setopt(nother, CURLOPT_SHARE, share);

    // std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
    assert(curl_easy_perform(nother) == CURLE_OK);
    // std::chrono::high_resolution_clock::time_point t4 = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> time_span0 = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1);
    // std::chrono::duration<double> time_span1 = std::chrono::duration_cast<std::chrono::duration<double>>(t4 - t3);
    std::cout << std::endl;
    std::cout << "time_span0: " << time_span0.count() << std::endl;
    // std::cout << "time_span1: " << time_span1.count() << std::endl;
    
    std::cout << headerPtr->size << std::endl;
    for (auto & header : headerPtr->headerVec) {
      std::cout << __LINE__ << " " << header << std::endl;
    }
    // std::cout << respond.size() << std::endl;
    // std::cout << chunk.response << std::endl;
    std::cout << respond << std::endl;
    // for (auto &ch : headerPtr->headerVec[headerPtr->headerVec.size() - 1]) {
    //   std::cout << static_cast<int>(ch) << " " << ch << std::endl;
    // }
    long response_code;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
    std::cout << "CURLINFO_RESPONSE_CODE:" << response_code << std::endl;
    long code;
    curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE, &code);
    std::cout << "CURLINFO_HTTP_CONNECTCODE:" << code << std::endl;
    long sockfd1;
    curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockfd1);
    std::cout << "CURLINFO_LASTSOCKET:" << sockfd1 << std::endl;
    std::cout << __LINE__ << std::endl;
    curl_easy_cleanup(nother);
    curl_easy_cleanup(curl);
    curl_slist_free_all(list);
    curl_slist_free_all(host);

    delete headerPtr;
  }
  return 0;
}

template<CURLINFO> struct info_t;
template<CURLINFO info> using Info_type = typename info_t<info>::type;
template<CURLINFO info> using Info_type1 = typename info_t<info>::type1;
template <> struct info_t<CURLINFO_EFFECTIVE_URL> {
  using type = int;
  using type1 = char *;
};
template <> struct info_t<CURLINFO_RESPONSE_CODE> {
  using type = float;
  using type1 = char *;
};
#include <string>
void EasyGet(std::string url) {
  curl_global_init(CURL_GLOBAL_ALL); 
  CURL *curl = curl_easy_init();
  CURLcode ret;
  std::cout << __LINE__ << __PRETTY_FUNCTION__ << url << std::endl;

  ret = curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
  std::cout << __LINE__ << __PRETTY_FUNCTION__ << ret << std::endl;
  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
  curl_easy_setopt(curl, CURLOPT_HEADER, 1);
  std::string respond;
  ret = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteStringCallback);
  std::cout << __LINE__ << __PRETTY_FUNCTION__ << ret << std::endl;
  ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&respond);
  std::cout << __LINE__ << __PRETTY_FUNCTION__ << ret << std::endl;

  std::cout << __LINE__ << __PRETTY_FUNCTION__ << url << std::endl;
  ret = curl_easy_perform(curl);

  std::cout << __LINE__ << __PRETTY_FUNCTION__ << url << std::endl;
  curl_easy_cleanup(curl);
  curl_global_cleanup();
}
int main() {
    // HelloCURL();
    // PostCURL();
    // ReUseCURL();
    EasyGet("https://example.com");
    // MimeType();
    Info_type<CURLINFO_EFFECTIVE_URL> a = 10;
    Info_type<CURLINFO_RESPONSE_CODE> f = 10.2;
    std::cout << a << std::endl;
    std::cout << f << std::endl;
    return 0;
}
